package com.maben.s9001.config;

import com.alibaba.druid.filter.Filter;
import com.alibaba.druid.filter.stat.StatFilter;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.wall.WallConfig;
import com.alibaba.druid.wall.WallFilter;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

@Configuration
@EnableTransactionManagement // 开启事务管理
@EnableJpaRepositories(
        basePackages = DataSourceJpaConfig.PACKAGE_REPOSITORY, // 只扫描自身的Repository
        transactionManagerRef = "jpaTransactionManager" // 绑定自身的事务管理器
)

public class DataSourceJpaConfig {
    public static final String PACKAGE_REPOSITORY = "com.maben.s9001.repository";
    private static final String PACKAGE_DOMAIN = "com.maben.s9001.domain";

    @Value("${spring.datasource.druid.url}")
    private String url;
    @Value("${spring.datasource.druid.password}")
    private String password;
    @Value("${spring.datasource.druid.username}")
    private String username;
    @Value("${spring.datasource.druid.driverClassName}")
    private String driverClassName;
    @Value("${spring.datasource.druid.initial-size}")
    private int initialSize;
    @Value("${spring.datasource.druid.min-idle}")
    private int minIdle;
    @Value("${spring.datasource.druid.max-active}")
    private int maxActive;
    @Value("${spring.datasource.druid.max-wait}")
    private int maxWait;
    @Value("${spring.datasource.druid.time-between-eviction-runs-millis}")
    private int timeBetweenEvictionRunsMillis;
    @Value("${spring.datasource.druid.validation-query}")
    private String validationQuery;
    @Value("${spring.datasource.druid.test-while-idle}")
    private boolean testWhileIdle;
    @Value("${spring.datasource.druid.test-on-borrow}")
    private boolean testOnBorrow;
    @Value("${spring.datasource.druid.test-on-return}")
    private boolean testOnReturn;

    /**
     * 配置数据库【数据源】
     */
    @Bean(name = "jpaDataSource")
    @Primary //多个数据源配置一个默认使用的
    public DataSource masterDataSource() {
        DruidDataSource druidDataSource = new DruidDataSource();
        //连接基础参数
        druidDataSource.setUrl(url);
        druidDataSource.setUsername(username);
        druidDataSource.setPassword(password);
        druidDataSource.setDriverClassName(driverClassName);
        //连接池设置
        druidDataSource.setMinIdle(minIdle);
        druidDataSource.setInitialSize(initialSize);
        druidDataSource.setMaxActive(maxActive);
        druidDataSource.setMaxWait(maxWait);
        druidDataSource.setValidationQuery(validationQuery);
        druidDataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
        druidDataSource.setTestWhileIdle(testWhileIdle);
        druidDataSource.setTestOnBorrow(testOnBorrow);
        druidDataSource.setTestOnReturn(testOnReturn);
        //支持批量更新设置
        List<Filter> filterList = new ArrayList<>();
        filterList.add(statFilter());
        filterList.add(wallFilter());
        druidDataSource.setProxyFilters(filterList);
        return druidDataSource;
    }

    @Value("${spring.datasource.druid.filter.wall.config.multi-statement-allow}")
    private boolean filterWallMultiStatementAllow;

    /**
     * wall（防SQL注入）
     */
    public WallFilter wallFilter() {
        WallFilter wallFilter = new WallFilter();
        WallConfig config = new WallConfig();
        config.setMultiStatementAllow(filterWallMultiStatementAllow);//允许一次执行多条语句
        wallFilter.setConfig(config);
        return wallFilter;
    }

    @Value("${spring.datasource.druid.filter.stat.log-slow-sql}")
    private boolean filterStatLogSlowSql;
    @Value("${spring.datasource.druid.filter.stat.slow-sql-millis}")
    private long filterStatSlowSqlMillis;
    @Value("${spring.datasource.druid.filter.stat.merge-sql}")
    private boolean filterStatMergeSql;

    /**
     * stat（监控统计）
     */
    public StatFilter statFilter() {
        StatFilter statFilter = new StatFilter();
        statFilter.setLogSlowSql(filterStatLogSlowSql);
        statFilter.setSlowSqlMillis(filterStatSlowSqlMillis);
        statFilter.setMergeSql(filterStatMergeSql);
        return statFilter;
    }


    // 在 s9001 的配置类中（如 S9001JpaConfig.java）
    @Bean(name = "entityManagerFactory")
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(@Qualifier("jpaDataSource") DataSource jpaDataSource) {
        LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
        factory.setDataSource(jpaDataSource);
        factory.setPackagesToScan("com.maben.s9001.domain");
        // 配置JPA供应商适配器（Hibernate）
        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        vendorAdapter.setShowSql(true);
        vendorAdapter.setGenerateDdl(true);
        factory.setJpaVendorAdapter(vendorAdapter);
        // 设置Hibernate属性
        Properties jpaProperties = new Properties();
        jpaProperties.put("hibernate.hbm2ddl.auto", "update");
        jpaProperties.put("hibernate.dialect", "org.hibernate.dialect.MySQL8Dialect");
        factory.setJpaProperties(jpaProperties);

        return factory;
    }

    // JPA事务管理器（仅用于s9001）
    @Bean(name = "jpaTransactionManager")
    public PlatformTransactionManager jpaTransactionManager(LocalContainerEntityManagerFactoryBean entityManagerFactory) {
        return new JpaTransactionManager(entityManagerFactory.getObject());
    }
}